#include "peakfilegenerator.h"
#include <math.h>
#include <QVector>
//
template <class T>
PeakFileGenerator<T>::PeakFileGenerator( QDataStream * file, qint64 samplPerChan, int chans, int samplPerBlock, DataFile::DataTypes dataType )
	:peakGen(0),peakFile(file),channels(chans),blockSize(samplPerBlock),
	dataWritten(0), min(0), max(0), index(0)
{
	min = new T[channels];
	max = new T[channels];
	qint64 lastLevelMaxSize = 4096*blockSize;
	double levelSize = samplPerChan;
	QVector<qint64> sizes;
	while( levelSize > lastLevelMaxSize ) {
		levelSize /= blockSize;
		sizes << (qint64)ceil( levelSize );
	}
	int levels = sizes.size();
	*peakFile << dataType << levels << blockSize << channels;
	savePos();
	qint64 levelPos = position + levels*2*sizeof(qint64); //first level position
	for( int i = 0; i < levels; i++ ) {
		*peakFile << levelPos << sizes[i]; //save position and level size (in samples per channel)
		levelPos += sizes[i]*2*channels*sizeof(T); //calculate next level position
	}
	if( levels > 0 ) {
		restorePos();
		*peakFile >> position >> dataCount;
	}
	if( levels > 1 ) {
		peakGen = new PeakFileGenerator( peakFile, levels-1, channels, blockSize );//
	}
}
//
template <class T>
PeakFileGenerator<T>::~PeakFileGenerator()
{
	if( peakGen )
		delete peakGen;
	if( min )
		delete[] min;
	if( max )
		delete[] max;
}
//
template <class T>
inline void PeakFileGenerator<T>::processSamples( T * sample )
{
	processData( sample, sample );
}
//
template <class T>
void PeakFileGenerator<T>::end()
{
	if( index != 0 ) {
		restorePos();
		if( dataWritten < dataCount ) {
			for( int i = 0; i < channels; i++ )
				*peakFile << min[i] << max[i];
			dataWritten++;
		}
		savePos();
		if( peakGen ) // generate next level
			peakGen->end();
	}
}
//
template <class T>
PeakFileGenerator<T>::PeakFileGenerator( QDataStream * file, int level, int chans, int samplPerBlock )
	:peakGen(0),peakFile(file),channels(chans),blockSize(samplPerBlock),
	dataWritten(0), min(0), max(0), index(0)
{
	min = new T[channels];
	max = new T[channels];
	*peakFile >> position >> dataCount;
	if( level > 1 )
		peakGen = new PeakFileGenerator( peakFile, level-1, channels, blockSize );//
}
//
template <class T>
inline void PeakFileGenerator<T>::savePos()
{
	position = peakFile->device()->pos();
}
//
template <class T>
inline void PeakFileGenerator<T>::restorePos()
{
	peakFile->device()->seek( position );
}
//
template <class T>
void PeakFileGenerator<T>::processData( T * minSample, T * maxSample )
{
	for( int i = 0; i < channels; i++ ) {
		if( index == 0 ) {
			min[i] = minSample[i];
			max[i] = maxSample[i];
		}
		else {
			if( min[i] > minSample[i] ) min[i] = minSample[i];
			if( max[i] < maxSample[i] ) max[i] = maxSample[i];
		}
	}
	index++;
	if( index == blockSize ) {
		index = 0;
		restorePos();
		if( dataWritten < dataCount ) {
			for( int i = 0; i < channels; i++ )
				*peakFile << min[i] << max[i];
			dataWritten++;
		}
		savePos();
		if( peakGen ) // generate next level
			peakGen->processData( min, max );
	}
}
//
template class PeakFileGenerator<qint8>;
template class PeakFileGenerator<quint8>;
template class PeakFileGenerator<qint16>;
template class PeakFileGenerator<quint16>;
template class PeakFileGenerator<qint32>;
template class PeakFileGenerator<quint32>;
template class PeakFileGenerator<float>;
template class PeakFileGenerator<double>;
